SUMMARY
 
Prayer Feed offers a simple and intuitive way to create and share prayer requests and receive requests from others. It also provides a centralized, organized, easy-to-access location for these requests, like a virtual prayer wall. Share your prayer requests privately (with only those people you choose), view all prayer requests quickly in a feed, and touch the hands when you pray to show that someone is praying!
 
 
FEATURES
 
• See all prayer requests in a single feed
• Touch the hands on a prayer request when you pray to increase the counter which everyone can see
• Filter prayer requests based on new today, urgent, bookmarked, private, answered, and more...
• When your prayer request is answered, swipe right to mark it answered--those praying will get the notification!
• Mark a request as urgent if you need prayers immediately
• Set an expiration date for the requests that have a deadline
• Set reminders for time specific requests (i.e. surgery, job interview, etc)
• Create groups of prayer partners to make sharing quick and simple
• Unlike Group Texts, there is NO LIMIT to the number of people with whom you can share at one time!
 
 
 
TECHNICAL HIGHLIGHTS

iOS:
  • Push notifications (new prayer request and answered prayer requests)
  • Local notifications (reminders)
  • In-app purchases
  • iAds
  • Address book integration
  • Auto layouts
  • Core data (storage and sorting)
  • Multiple contexts (for handling client/server interaction without crashing the UI)
  • Offline mode
  • Background retry of pending requests
 
Client/Server & DB:
  • SSL with 2048-bit certificate
  • DB data encryption
  • APNS hosting
  • Cron job for processing APNS requests
  • Cloud hosting via big name provider
  • Token-based sessions that expire
  • Reset password via email with timeout

 
CHALLENGES
 
Focus
To provide an end-to-end solution for managing prayer requests in a modern-day "journal." Considering I did all of the work on my own, I had to accept some tradeoffs to get it done. In future updates, I'd like to focus on usability, but the primary goal for v1 was to a working, usable solution with significant attention on high availability, offline scnearios and protected data.
 
iOS
While this may look simple at first glance, the iOS client app was a significant amount of work.

Server & Device Sync + Offline Mode
Syncing data with the server and other devices was, by far, the most challenge aspect of the project. The challenge isn't so much the asynchonous work to be done (so the UI doesn't freeze), but moreso making sure that pending requests are sent as soon as possible (like in the instance of network failure or data stored while offline) and reconciling the difference, while making it seemless to the user. 
 
First, I had to choose the method for communicating to the server (decided on JSON) and had to create the code to handle serializing and deserializing the objects (both client side and sever side, since the DB storeage between the server and Core Data was not the same). This also required ironing out a first draft of the API, which would change later to decrease bandwidth and improve efficiency.
 
Second, I had to consider the tradeoffs of data stored vs data presented in the UI. Data being retrieved in the background can't automatically be modified to the main context, because that could cause inconsistencies in the UI and result in a crash. This forced the need for separate contexts. This one of the big benefits of Core Data--contexts allow certain controls in the UI to modify data being presented, with the ability to persist it later for smooth interaction (like scrolling) in the UI. 
 
Then I needed to consider how to handle deletes--would it be based on a flag, or stored elsewhere? This affects filtering--we either have to filter out data in the main context, or store it separately. I chose to store it separately for several reasons: (1) easier filtering; (2) speed of data lookups (nothing old polluting the DB and complicating queries); (3) no need to keep a separate "deleted" state; (4) no concerns for modifying the main context when deleting the data later; (5) can simply syncronization requests by having one less step in the process that affects the user.
 
Next, I focused on the process for what data to sync from the server. There are several things to get from the server that is required for the full object model, such people, prayer requests, metadata, deleted data (more later in "multiple devices"), etc.The easiest way to handle this is serializing the whole entity along with relationships, which I did at first. This was my "iterative" approach so I could make progress and get working code to solicit feedback early (Agile, anyone?).
 
Once syncing with the server was done, I focused on other parts fo the app. I wanted to focus on getting feedback rather than coding too much up front and need to change it later. By doing this, I discovered that, no only did I need to make the communication more efficient, but I also discovered that order really matters. (To note, I knew I would need to not send everything every time, but I wasn't sure if I'd need to tease out the ability to send small bits of metadata separately--and I did). 
 
Back to the data syncing, I now focused on sending only data that had changed. I did this using state at the object level and periodically checking for pending data. When found, the data would be processed in the background. I also identified the small handful of items that needed to be sent separately, such as bookmarks, "prayed for you" touches, marked answered, etc. These things are small don't require overall udpates to the objects themselves. Note that MOST communication requires a handshake from the server before being marked "saved." Getting data out of sync could result in lost data, which wouldn't be acceptable.
 
Before I got to the order of what was processed first, I decided to focus on syncing across devices. This was a natural next step, because what I found mattered the most was the small metadata items between the devices. It's easy enough to find new objects that don't exist, but ensuring the state of pieces of those objects took a bit more work.
 
Finally, I got to the order. The order proved to be very problematic. Rather than go through all the various scenarios, suffice it to say I had to consider new prayer requests, updated ones, deleted ones, times prayed for, bookmarked, hidden, along with people, groups, ignored people, etc. And if you update one before updating the other, presentation could be seriously affected (such as if you added a prayer partner from one device and then shared a request to them--I must download the new person before the prayer request, or the request might be displayed before we have the info on who sent it).
 
When all was said and done, the system worked well: no data loss, fast communication, no presentation issues, and data synced across devices.
 
The Cell
Designing the cell was a challenge--it's not easy to incorporate all the interactions you want to provide to users while keeping the cell clean and easy to digest. I utilized both touch actions along with swipe gestures to provide as much as made sense.
 
The must-have features, at minimum, were: requester name, requester email, and the prayer request text.
 
The strong-want features were: tap to mark prayed (the praying hands), a way to designate if this was private or shared, the date last prayed, and the photo (because it really adds the personal connection when see the requester or whatever image you have chosen to represent them). Another was auto-resizing of cells, though I'm rethinking this after the first release.
 
The nice-to-have features were: Bookmarking (for filtering), easy way to mark answered (swiping), add reminders (in time-sensitive cases, like surgery or job interview), a way to delete (swipe), and adding someone as a prayer partner (tap cell).
 
More to come later...
Prayer Feed
Published:

Prayer Feed

Goal: To provide an end-to-end solution for managing prayer requests in a modern-day "journal." Prayer Feed offers a simple and intuitive way to Read More

Published: